#include <my_header.h>
#include "netfd_queue.h"
#include "thread_pool.h"
#include "worker_thread_info.h"
#include "init_tcp.h"
#include "epoll.h"
#include "worker.h"
#include "register_login.h"
#include "jwt.h"
#include "timeout_kick.h"
#include <signal.h>
#include <sys/wait.h>
//这里采用异步拉起同步机制,这个管道用来父进程与子进程进行通信使用,如果父进程
//收到了10号新号,就会给子进程发送退出表示,这样子进程再给若干个子线程发送flag=1标志位
//若干个子线程退出后,子进程才能退出,子进程退出后父进程才能退出
int exit_pipe[2];
void hanlder(int signal_num){
    printf("the server got a %d signal,the server is going to exit\n",signal_num);
    write(exit_pipe[1],"exit",4);//给子进程发送退出信息
}
void print_banner(){
    printf("————————————————————————————————————————————————————————————————————-- \n");
    printf("//                           _ooOoo_                                  //\n");
    printf("//                          o8888888o                                //\n");
    printf("///                         88~ . ~88                                //\n");
    printf("///                         (| ^_^ |)                                //\n");
    printf("///                         O\\  =  /O                               //\n");
    printf("///                      ____/`---'\\____                            //\n");
    printf("///                    .'  \\|     |//  `.                           //\n");
    printf("///                   /  \\|||  :  |||//  \\                         //\n");
    printf("///                  /  _||||| -:- |||||- \\                         //\n");
    printf("///                  |   | \\\  -  /// |   |                         //\n");
    printf("///                  | \_|  ''\---/''  |   |                         //\n");
    printf("///                  \  .-\__  `-`  ___/-. /                         //\n");
    printf("///                ___`. .'  /--.--\  `. . ___                       //\n");
    printf("///              ."" '<  `.___\_<|>_/___.'  >'"".                    //\n");
    printf("///            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                   //\n");
    printf("///            \  \ `-.   \_ __\ /__ _/   .-` /  /                   //\n");
    printf("///      ========`-.____`-.___\_____/___.-`____.-'========           //\n");
    printf("///                           `=---='                                //\n");
    printf("///      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^          //\n");
    printf("///            佛祖保佑       永不宕机     永无BUG                   //\n");
    printf("/———————————————————————————————————————————————————————————————————— \n");
}

int main(int argc, char *argv[]){
    ARGS_CHECK(argc,4);
    pipe(exit_pipe);
    int fork_ret=fork();
    if(fork_ret){
        //父进程
        close(exit_pipe[0]);
        signal(SIGUSR1,hanlder);
        wait(NULL);
        exit(EXIT_SUCCESS);
    }
    close(exit_pipe[1]);
    if (chdir(ROOTDIR) == -1) {
        perror("chdir to ROOTDIR failed");
        printf("请确保目录存在：%s\n", ROOTDIR);
        exit(1);
    }
    //子进程
    int init_worker_num=atoi(argv[3]);//获取初始化工人线程数量
    int sockfd=init_tcp(argv[1],argv[2]);//建立TCP连接
    int epfd=epoll_create(1);//创建epoll
    epoll_add(epfd,sockfd);//监听sockfd,连接用户
    epoll_add(epfd,exit_pipe[0]);//这个管道是父进程与子进程通信使用的管道如果父进程收到了退出的信号会想子进程发送一个flag
    
    my_stack dirStackArr[1024]; 
    int fdToIdx[1024] = {0}; //fdToIdx[fd]=idx
    thread_pool_info_t thread_pool_info;//创建共享资源,包含工人信息,锁,条件变量,连接netfd队列,退出表示等信息
    init_thread_pool_info(&thread_pool_info,init_worker_num,dirStackArr,fdToIdx);//初始化这些共享资源,但不创建工人线程
    print_banner();
    create_worker(&thread_pool_info);//创建工人线程

    struct epoll_event epoll_res[1];
    int netfds[1024]={0};
    int netfd_idx=0;
    train_t train;

    /* int userReady[1024] = {0}; */
    int idx = 0;
    int netfd=0;

    char token[512]={0}; //用户身份认证token
    MYSQL* mysql=get_one_mysql_connect(); //连接数据库表
    TimeWheel tw;
    timewheel_init(&tw);
    char temp_path[1024]={0}; //
                              //用于mkdir.rmdir
    char temp_file_name[512]={0};
    //用于返回给客户端值的存储
    char temp_ret[1024]={0};
    char recent_cmd[1024]={0};
    int  recvn_ret;

    int remove_netfd=-1;
    while(1){
        int n = epoll_wait(epfd,epoll_res,1,1000);
        if(n == 0){
            timewheel_tick(&tw,&remove_netfd);
            print_current_time();
            print_active_connections(&tw);
            //移除对netfd的监听
            //TODO:客户端发送退出的指令
            if(netfd!=-1){
                epoll_del(epfd,remove_netfd);
                close(remove_netfd);
            }
            continue;
        }
        // 客户端连接
        if(epoll_res[0].data.fd==sockfd){
            netfds[netfd_idx] =accept(sockfd,NULL,NULL);
            printf("accept a new netfd= %d\n", netfds[netfd_idx]);
            get_current_time();
            timewheel_add_connection(&tw,netfds[netfd_idx]);
            epoll_add(epfd,netfds[netfd_idx]); // 监听netfd
            netfd_idx++;

        }
        else if(epoll_res[0].data.fd==exit_pipe[0]){
            //子进程收到父进程发来的退出标志,修改thread_pool_info中的flag,子线程这个时候要不在发送文件
            //要不在等待netfd,当监听到flag=1的时候就会自己退出线程,清理资源
            printf("I am the child process I got a exit signal from father process\n");
            pthread_mutex_lock(&thread_pool_info.mutex);
            thread_pool_info.exit_flag=1;
            pthread_cond_broadcast(&thread_pool_info.cond);//修改flag的值这样子线程就回到了系统正要退出,子线程完成自己的任务后就会自行退出
            pthread_mutex_unlock(&thread_pool_info.mutex);
            for(int i=0;i<thread_pool_info.worker_info.worker_num;i++){
                //子进程的退出条件是所有的子线程全部退出后才能退出
                pthread_join(thread_pool_info.worker_info.worker_arr[i],NULL);
            }
            printf("all the threads had quited,I am the child process,I have quited too\n");
            exit(EXIT_SUCCESS);
        }
        else{
            for(int i=0;i<1024;i++){
                if(epoll_res[0].data.fd==netfds[i]){
                    netfd=netfds[i];
                    break;
                }
            }
            bzero(&train,sizeof(train));
            //timeout_kick暂定
            timewheel_activate_connection(&tw,netfd);

            //测试timeout_kick
            //print_current_time();
            //print_active_connections(&tw);


            //接收客户端命令并处理
            recv(netfd,&train.type,sizeof(train.type),MSG_WAITALL);
            printf("recv train.type = %d\n",train.type);

            if(train.type==USER_REGISTER){
                recv(netfd,&train.length,sizeof(train.length),MSG_WAITALL);
                recv(netfd,train.data,train.length,MSG_WAITALL);

                // train.data==user_name_password
                user_register(mysql,netfd,train.data);
                continue;
            }
            if(train.type==USER_LOGIN){
                // train.data==user_name_password
                /* printf("user login\n"); */
                recv(netfd,&train.length,sizeof(train.length),MSG_WAITALL);
                recv(netfd,train.data,train.length,MSG_WAITALL);
                /* printf("user login train.data = %s\n", train.data); */

                char user_name[128]={0};
                int uret=user_login(mysql,netfd,train.data,user_name);
                if(uret!=0) continue;

                //登录成功
                //采用jwt加密算法根据username和salt生成token
                get_jwt(token,user_name,30000);
                /* printf("token = %s\n", token); */
                //发送token
                bzero(train.data,sizeof(train.data));
                train.type=USER_TOKEN;
                train.length=strlen(token);
                memcpy(train.data,token,train.length);
                send(netfd,&train,sizeof(train.type)+sizeof(train.length)+train.length,0);
                bzero(train.data,train.length);

                //发送旧的netfd
                send(netfd,&netfd,sizeof(netfd),0);

                //创建目录栈
                stack_init(&dirStackArr[idx]);
                fdToIdx[netfd]=idx;
                int ret = user_init(user_name,mysql,&dirStackArr[idx]);
                /* printf("user_init ret = %d\n", ret); */

                //初始化后打印栈信息
                /* printf("login after dirStackArr[idx] = "); */
                /* print_stack(dirStackArr[idx]); */

                idx++;
                continue;
            }
            if(train.type==USER_TOKEN){
                // 接收用户名
                char user_name[128]={0};
                recv(netfd,&train.length,sizeof(train.length),MSG_WAITALL);
                recv(netfd,train.data,train.length,MSG_WAITALL);
                memcpy(user_name,train.data,train.length);

                // 接收token值
                char recv_token[512]={0};
                recvn(netfd,&train.type,sizeof(train.type));
                recvn(netfd,&train.length,sizeof(train.length));
                recvn(netfd,train.data,train.length);
                memcpy(recv_token,train.data,train.length);


                char judge_token[512]={0};
                get_jwt(judge_token,user_name,300000);
                int vret=valid_jwt(judge_token,user_name);
                if(vret!=0){
                    /* int wrong=1; */
                    /* send(netfd,&wrong,sizeof(int),0); */
                    continue;
                }
                /* int wrong=0; */
                /* send(netfd,&wrong,sizeof(int),0); */

                // 接收旧的netfd，共享给工人线程，加锁
                int old_netfd;
                recvn(netfd,&old_netfd,sizeof(old_netfd));
                // 接收命令
                /* bzero(train.data,sizeof(train.data)); */
                /* recvn(netfd,&train.type,sizeof(train.type)); */
                /* recvn(netfd,&train.length,sizeof(train.length)); */
                /* recvn(netfd,train.data,train.length); */
                // gets or puts 交给工人线程处理
                pthread_mutex_lock(&thread_pool_info.mutex);
                push_back_netfd(&thread_pool_info.netfd_queue,netfd,old_netfd);
                pthread_cond_broadcast(&thread_pool_info.cond);
                pthread_mutex_unlock(&thread_pool_info.mutex);
                continue;
            }
            if(train.type==CMD_CD){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;

                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name);

                // 调用cd方法,正确返回type=SEND_URL路径,不正确返回tip,type=SEND_TIP
                bzero(&train.length,sizeof(train.length));
                recvn_ret= recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}

                bzero(train.data,train.length);
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}

                bzero(temp_path,sizeof(temp_path));
                bzero(temp_ret,sizeof(temp_ret));
                strcpy(temp_path,train.data);
                printf("--cd--%d,%s\n",train.length,temp_path);

                int cd_ret=handle_cd(user_name,mysql,&dirStackArr[index],temp_path,temp_ret);
                if(cd_ret==0){
                    bzero(&train,sizeof(train));
                    train.type=SEND_URL;
                    train.length=strlen(temp_ret);
                    strcpy(train.data,temp_ret);
                    send(netfd,&train,sizeof(train.type)+sizeof(train.length)+train.length,0);
                    log_info(mysql,current_user_id,"cd",1,temp_path);
                }else{
                    send_tips(netfd,"错误路径!");
                    log_info(mysql,current_user_id,"cd",0,temp_path);
                }
                continue;
            }
            if(train.type==CMD_LS){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;
                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name);
                //调用ls方法,正确返回type=SEND_FILE_DIR 路径,不正确返回tip,type=SEND_TIP
                recvn_ret=recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                bzero(temp_path,sizeof(temp_path));
                bzero(temp_ret,sizeof(temp_ret));
                strcpy(temp_path,train.data);
                printf("--ls--%d,%s\n",train.length,temp_path);
                int ls_ret=ls(mysql,&dirStackArr[index],temp_ret,sizeof(temp_ret),temp_path,user_name);
                if(ls_ret==0){
                    bzero(&train,sizeof(train));
                    train.type=SEND_FILE_DIR;
                    train.length=strlen(temp_ret);
                    strcpy(train.data,temp_ret);
                    /* printf("data:%s\n",train.data); */
                    /* printf("length:%d\n",train.length); */
                    /* printf("%s\n",train.data); */
                    send(netfd,&train,sizeof(train.type)+sizeof(train.length)+train.length,0);
                    log_info(mysql,current_user_id,"ls",1,temp_path);
                }else{
                    send_tips(netfd,"错误路径!");
                    log_info(mysql,current_user_id,"ls",0,"wrong path!");
                } 
                continue;
            }
            if(train.type==CMD_PWD){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;
                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name);
                recvn_ret=recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                printf("--pwd--%d\n",train.length);
                // 调用pwd方法,正确返回type=SEND_URL
                bzero(temp_ret,sizeof(temp_ret));
                pwd(mysql,&dirStackArr[index],temp_ret,sizeof(temp_ret));
                send_url(netfd,temp_ret);
                log_info(mysql,current_user_id,"pwd",1,"");
                continue;
            }
            if(train.type==CMD_REMOVE){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;
                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name); // 调用remove方法,正确返回tip type=SEND_TIP 路径,不正确返回tip,type=SEND_TIP
                recvn_ret= recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                bzero(temp_file_name,sizeof(temp_file_name));
                bzero(temp_ret,sizeof(temp_ret));
                strcpy(temp_file_name,train.data);
                printf("--remove--%d,%s\n",train.length,temp_file_name);
                int remove_ret=remove_file(mysql,&dirStackArr[index],temp_ret,sizeof(temp_ret),temp_file_name,user_name);
                if(remove_ret==0){
                    send_tips(netfd,"删除成功!");
                    log_info(mysql,current_user_id,"remove",1,temp_file_name);
                }else{
                    send_tips(netfd,temp_ret);
                    log_info(mysql,current_user_id,"remove",0,temp_file_name);
                }
                continue;
            }
            if(train.type==CMD_MKDIR){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;
                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name);
                //调用mkdir方法,正确返回tip type=SEND_TIP 路径,不正确返回tip,type=SEND_TIP
                recvn_ret=recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                bzero(temp_file_name,sizeof(temp_file_name));
                strcpy(temp_file_name,train.data);
                printf("--mkdir--%d,%s\n",train.length,temp_file_name);
                int mkdir_ret=handle_mkdir(user_name,mysql,&dirStackArr[index],temp_file_name);
                if(mkdir_ret==0){
                    send_tips(netfd,"创建成功!");
                    log_info(mysql,current_user_id,"mkdir",1,temp_file_name);
                }else if(mkdir_ret==1){
                    send_tips(netfd,"创建失败,不能有重复的目录!");
                    log_info(mysql,current_user_id,"mkdir",0,"can not has the same dir!");
                }else if(mkdir_ret==-1){
                    send_tips(netfd,"创建失败,目录格式不对!");
                    log_info(mysql,current_user_id,"mkdir",0,"the dir is not right!");
                }
                continue;
            }
            if(train.type==CMD_RMDIR){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;
                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name);
                // 调用rmdir方法,正确返回tip type=SEND_TIP 路径,不正确返回tip,type=SEND_TIP
                recvn_ret=recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                bzero(temp_file_name,sizeof(temp_file_name));
                strcpy(temp_file_name,train.data);
                printf("--rmdir--%d,%s\n",train.length,temp_file_name);
                int rmdir_ret=handle_rmdir(user_name,mysql,&dirStackArr[index],temp_file_name);
                if(rmdir_ret==0){
                    send_tips(netfd,"删除成功!");
                    log_info(mysql,current_user_id,"rmdir",1,temp_file_name);
                }else{
                    send_tips(netfd,"删除失败,目录不为空!");
                    log_info(mysql,current_user_id,"rmdir",0,temp_file_name);
                }
                continue;
            }
            if(train.type==CMD_HISTORY){
                char user_name[128]={0}; // 用户名
                                         //netfd->stacktop栈id->查表->username
                int index = fdToIdx[netfd];
                int vir_dire_id=dirStackArr[index].stack_top->id;
                get_user_name_by_vid(mysql,vir_dire_id,user_name);
                int current_user_id=get_user_id_by_user_name(mysql,user_name);
                recvn_ret=recvn(netfd,&train.length,sizeof(train.length));
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                recvn_ret=recvn(netfd,train.data,train.length);
                if(recvn_ret==-1){printf("netfd quit!%d\n",netfd);continue;}
                bzero(recent_cmd,sizeof(recent_cmd));
                printf("--history--%d\n",train.length);
                int ret=recent_use_cmd(mysql,recent_cmd,current_user_id);
                if(ret){
                    send_tips(netfd,recent_cmd);
                }else{
                    send_tips(netfd,"最近没有使用过任何命令!");
                }
                log_info(mysql,current_user_id,"history",1,recent_cmd);
                continue;
            }
        }
    }
    return 0;
}

